﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using ZaidimasSandelys.Models.DataStructures;

namespace ZaidimasSandelys.CustomDrawTools
{
    /// <summary>
    /// Žmogeliuko orientacija. Fiksuotos reikšmės suteiktos sukimo kampo skaičiavimui
    /// </summary>
    public enum PersonOrientation : short
    {
        Right = 0,      // Rankos nukreiptos į dešinę
        Down = 1,     // Rankos nukreiptos žemyn
        Left = 2,       // Rankos nukreiptos į kairę
        Top = 3         // Rankos nukreiptos aukštyn
    }

    public class PersonVisual : AllocatedVisual
    {
        private static readonly string[] brushIdListPerson = new string[] 
                    { "soko.png", "soko90.png", "soko180.png", "soko270.png" };
        private static readonly string[] brushIdListAutocar = new string[] 
                    { "autokaras.png", "autokaras90.png", "autokaras180.png", "autokaras270.png" };
        private static readonly string[] brushIdListAutocarWithContainer = new string[] 
                    { "autokaras-trumpas.png", "autokaras90-trumpas.png", "autokaras180-trumpas.png", "autokaras270-trumpas.png" };
        private static readonly string[] brushIdListAutocarBack =
            new string[] { "autokaras-atbulas.png", "autokaras90-atbulas.png", 
                           "autokaras180-atbulas.png", "autokaras270-atbulas.png" };
        private static readonly string[] brushIdListAutocarWithContainerBack =
            new string[] { "autokaras-atbulas-trumpas.png", "autokaras90-atbulas-trumpas.png", 
                           "autokaras180-atbulas-trumpas.png", "autokaras270-atbulas-trumpas.png" };

        /// <summary>
        /// Kurion pusėn judės žmogeliukas
        /// </summary>
        public PersonOrientation FlowDirection { get; private set; }
        /// <summary>
        /// Elementų sąrašas reikalingas judėjimo laisvei apriboti
        /// </summary>
        public DrawingCanvas AllGameElements { get; set; }
        /// <summary>
        /// <code>true</code> - Žmogeliukas įlipo į autokarą
        /// </summary>
        private bool _isInsideAutocar;
        /// <summary>
        /// <code>false</code> - autokaras veža dėžę
        /// </summary>
        private bool _isAutocarEmpty;

        /// <summary>
        /// <code>true</code> - autokaras juda atbulas
        /// </summary>
        private bool _autocarMovesBack;

        /// <summary>
        /// Pasukimo kampas
        /// </summary>
        private int _angle;
        /// <summary>
        /// Atstumo daugiklis
        /// </summary>
        protected Size _multiplicator;

        private MovementTest mTest = null;

        public PersonVisual(short x, short y) : base(x, y, VisualType.Person)
        {
            this.CanMove = true;
            this.FlowDirection = PersonOrientation.Right;
            this._isInsideAutocar = false;
            this._isAutocarEmpty = true;
        }

        public override void DrawItself(Size multiplicator)
        {
            this._multiplicator = multiplicator;
#if DEBUG
            this.Dispatcher.VerifyAccess();
#endif
            DrawItself(this._multiplicator, GetBitmapBrush(null), new Pen(Brushes.Gray, 1));
        }

        /// <summary>
        /// <code>true</code> - žmogeliukas juda, <code>false</code> - žmogeliukas pasisuka
        /// </summary>
        /// <param name="newDirection">Nauja kryptis <see cref="PersonOrientation"/></param>
        /// <returns><code>true</code> - žmogeliukas juda, <code>false</code> - žmogeliukas pasisuka</returns>
        public bool ProcessKeyDown(PersonOrientation newDirection)
        {
            bool rzlt = false;
            _autocarMovesBack = false;
            _angle = GetAngle(newDirection);
            if (_angle != 0)
            {
                FlowDirection = newDirection;
                this._isAutocarEmpty = true;
            }
            else
            {
                rzlt = true;
                if (this._isInsideAutocar)
                    DriveAutocar();
                else
                    MoveImage();
            }
            DrawItself(this._multiplicator, GetBitmapBrush(null), new Pen(Brushes.Gray, 1));
            return rzlt;
        }

        public bool CanMoveCarBackExecute()
        {
            bool rzlt = false;
            if (this._isInsideAutocar)
            {
                Movement delta = MovementTest.CalculateMove(this.FlowDirection, true);
                Movement nextPoint = this.Position + delta;
                bool isOcupated = AllGameElements
                    .Where(x => x.Position == nextPoint)
                    .Any(x => !(x.ImgType == VisualType.Floor || x.ImgType == VisualType.Target || x.ImgType == VisualType.TargetHC));
                rzlt = !isOcupated;
            }
            return rzlt;
        }

        public void MoveAutocarBack()
        {
            _autocarMovesBack = true;
            Movement delta = MovementTest.CalculateMove(this.FlowDirection, true);
            this.Position = this.Position + delta;
            if (mTest != null && mTest.ContainerOrAutocar != null)
            {
                mTest.ContainerOrAutocar.RemoveAll(x => ((ContainerVisual)x).IsContainerOnTarget);
                this._isAutocarEmpty = mTest.ContainerOrAutocar.Count < 1;
                if (!this._isAutocarEmpty)
                {
                    var container = (ContainerVisual)mTest.ContainerOrAutocar[0];
                    container.Move(delta, this._multiplicator);
                }
            }
            DrawItself(this._multiplicator, GetBitmapBrush(null), new Pen(Brushes.Gray, 1));
        }

        #region Private methods
        private void DriveAutocar()
        {
            mTest = new MovementTest(this.FlowDirection, this.Position, this.AllGameElements, this._isInsideAutocar);
            this._isAutocarEmpty = mTest.ContainerOrAutocar == null || mTest.ContainerOrAutocar.Count < 1;
            if (mTest.CanMove)
            {
                Movement delta = MovementTest.CalculateMove(this.FlowDirection);
                if (mTest.ContainerOrAutocar != null)
                {
                    for (int i = mTest.ContainerOrAutocar.Count - 1; i >= 0; i--)
                    {
                        var container = mTest.ContainerOrAutocar[i] as ContainerVisual;
                        if (container != null) container.Move(delta, this._multiplicator);
                    }
                }
                this.Position = this.Position + delta;
            }
        }

        protected override Brush GetBitmapBrush(string name)
        {
            string imageName;
            if (this._isInsideAutocar)
            {
                if (_autocarMovesBack)
                {
                    imageName = this._isAutocarEmpty
                                    ? brushIdListAutocarBack[(short)FlowDirection]
                                    : brushIdListAutocarWithContainerBack[(short)FlowDirection];
                }
                else
                {
                    imageName = this._isAutocarEmpty
                                    ? brushIdListAutocar[(short)FlowDirection]
                                    : brushIdListAutocarWithContainer[(short)FlowDirection];
                }
            }
            else
                imageName = brushIdListPerson[(short)FlowDirection];
            return base.GetBitmapBrush(imageName);
        }

        private void MoveImage()
        {
            MovementTest mTest = new MovementTest(this.FlowDirection, this.Position, this.AllGameElements, this._isInsideAutocar);
            if (mTest.CanMove)
            {
                Movement delta = MovementTest.CalculateMove(this.FlowDirection);
                this.Position = this.Position + delta;
                if (mTest.ContainerOrAutocar != null)
                {
                    if (!mTest.IsInsideAutocar) // Pastumti dėžę
                        ((ContainerVisual)mTest.ContainerOrAutocar[0]).Move(delta, this._multiplicator);
                    else if (!this._isInsideAutocar)    // Įlipti į autokarą
                    {
                        this._isInsideAutocar = mTest.IsInsideAutocar;
                        this.AllGameElements.DeleteVisual(mTest.ContainerOrAutocar[0]);
                    }
                }
            }
        }

        private void RotateImage(int angle)
        {
            var rotate = new RotateTransform(angle)
            {
                CenterX = (this.Position.Width + 0.5) * this._multiplicator.Width,
                CenterY = (this.Position.Height + 0.5) * this._multiplicator.Height
            };
            this.Transform = rotate;
        }

        private int GetAngle(PersonOrientation newDirection)
        {
            if (newDirection == this.FlowDirection)
                return 0;

            short clockWiseSteps = 0, counterClockSteps = 0;
            short targetAngle = (short)newDirection;
            short iMax = (short)PersonOrientation.Top;
            short iMin = (short)PersonOrientation.Right;
            for (short i = (short)FlowDirection; i != targetAngle; )
            {
                clockWiseSteps++;
                if (i < iMax)
                    i++;
                else
                    i = iMin;
            }
            for (short i = (short)FlowDirection; i != targetAngle; )
            {
                counterClockSteps++;
                if (i > iMin)
                    i--;
                else
                    i = iMax;
            }
            return (clockWiseSteps <= counterClockSteps ? clockWiseSteps : -counterClockSteps) * 90;
        }
        #endregion
    }

    public class AutocarVisual : AllocatedVisual
    {
        protected Size _multiplicator;

        public AutocarVisual(short x, short y) : base(x, y, VisualType.Autocar)
        {
            this.ImgType = VisualType.Autocar;
            this.CanMove = true;
        }

        public override void DrawItself(Size multiplicator)
        {
            this._multiplicator = multiplicator;
#if DEBUG
            this.Dispatcher.VerifyAccess();
#endif
            DrawItself(multiplicator, GetBitmapBrush("autokaras-02.png"), new Pen(Brushes.Gray, 1));
        }
    }
}
